package edu.unc.ils.parse;

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Parses an Agricola MARC XML file
 */
public class AgricolaParser 
{
    public static void main(String[] args) throws Exception 
    {
        // Get the options specified
        CommandLineParser parser = new BasicParser( );

        Options options = getOptions();
        CommandLine commandLine = parser.parse( options, args );

        if (commandLine.hasOption("h"))
        {
            // Print the help message
            HelpFormatter formatter = new HelpFormatter();
            formatter.printHelp( "java " + AgricolaParser.class.getCanonicalName(), options );
        }
        else
        {
            String filePath = commandLine.getOptionValue("f");
            boolean doSubjects = commandLine.hasOption("s");
            boolean doTitle = commandLine.hasOption("t");
            boolean doAbstract = commandLine.hasOption("a");
            
            File file = new File(filePath);
            
            AgricolaParser ap = new AgricolaParser(file);
            
            if (doSubjects)
                ap.printSubjects();
            if (doTitle)
                ap.printTitle();
            if (doAbstract)
                ap.printAbstract();
        }
    }
    
    /**
     * Returns the CLI options
     * @return
     */
    public static Options getOptions()
    {
        Options options = new Options();
        Option filePath = new Option("f", true, "Path to MARC-XML formatted file");
        filePath.setRequired(false);
        options.addOption(filePath);
        
        options.addOption("h", false, "Print this help message");
        options.addOption("s", false, "Output subject headings");
        options.addOption("t", false, "Output title");
        options.addOption("a", false, "Output abstract");
       
        return options;
    }
    
    
    Document doc;
    XPath xPath;
    public AgricolaParser(File marcXmlFile) throws Exception
    {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        doc = db.parse(new FileInputStream(marcXmlFile));
        XPathFactory  factory=XPathFactory.newInstance();
        xPath=factory.newXPath();   
    }
    /**
     * Given a MARC XML formatted file, returns a list of subject headings
     * @param marcXmlFile
     * @return
     * @throws Exception
     */
    public List<String> getHeadings() throws Exception
    {
        List<String> subjects = new ArrayList<String>();
        
        // $650 $a
        NodeList nodeList = (NodeList)xPath.evaluate("/collection/record/datafield[@tag='650']/subfield[@code='a']",
                doc, XPathConstants.NODESET);
       
        for (int j=0; j<nodeList.getLength(); j++)
        {
            Node node = nodeList.item(j);
            String subject = node.getFirstChild().getNodeValue();
            if (!subjects.contains(subject))
                subjects.add(subject);
        }
        
        // Geographic
        nodeList = (NodeList)xPath.evaluate("/collection/record/datafield[@tag='651']/subfield[@code='a']",
                doc, XPathConstants.NODESET);
        for (int j=0; j<nodeList.getLength(); j++)
        {
            Node node = nodeList.item(j);
            String subject = node.getFirstChild().getNodeValue();
            if (!subjects.contains(subject))
                subjects.add(subject);
        }
        
                
        return subjects;
    }
    
    public String getAbstract() throws Exception
    { 
        // 520 $a
        NodeList nodeList = (NodeList)xPath.evaluate("/collection/record/datafield[@tag='520']/subfield[@code='a']",
                doc, XPathConstants.NODESET);
        String abs = "";
        for (int j=0; j<nodeList.getLength(); j++)
        {
            Node node = nodeList.item(j);
            abs += node.getFirstChild().getNodeValue();
        }
                
        return abs;
    }
    
    public String getTitle() throws Exception
    {
        // 245 $a = title
        NodeList nodeList = (NodeList)xPath.evaluate("/collection/record/datafield[@tag='245']/subfield[@code='a']",
                doc, XPathConstants.NODESET);
       
        String title = "";
        for (int j=0; j<nodeList.getLength(); j++)
        {
            Node node = nodeList.item(j);
            title = node.getFirstChild().getNodeValue();
        }
        
        return title;
    }
    
    public String getURL() throws Exception
    { 
        // URL
        NodeList nodeList = (NodeList)xPath.evaluate("/collection/record/datafield[@tag='856']/subfield[@code='u']",
                doc, XPathConstants.NODESET);
        String url = "";
        for (int j=0; j<nodeList.getLength(); j++)
        {
            Node node = nodeList.item(j);
            url = node.getFirstChild().getNodeValue();
        }
        return url;
    }
    
    /**
     * Prints a list of subject headings to standard out.
     * @param marcXmlFile
     * @throws Exception
     */
    public void printSubjects() throws Exception
    {
        List<String> subjects = getHeadings();
        for (String subject: subjects)
            System.out.println(subject);
    }
    
    public void printTitle() throws Exception
    {
        System.out.println(getTitle());
    }
    
    public void printAbstract() throws Exception
    {
        System.out.println(getAbstract());
    }
}
